/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin			*/
/*	Project:	GlbAdpTanx-1.0.0		*/
/*	Author:		gong_libin			*/
/*	Date:		2016_06_01			*/
/*	File:		GlbAdpTanx.cpp			*/
/********************************************************/

#include "GlbAdpTanx.h"

static UINT sg_uiMsg;
static UINT sg_uiTotal;
static GLBATCONF_S sg_stConf;
static GlbIpc::CGlbMsg* sg_pCMsg;
static GlbCls::CGlbPool sg_CPool;

int main(int argc, char* argv[])
{
	int iCount = 0;
 	int iIndex = 0;
	int iReturn = 0;
	int iThread = 1;
	GlbNet::CGlbNet CNet;
	GlbNet::CGlbEpoll CEpoll;
	GlbCls::CGlbThread CCache;
	char* pszPort = const_cast<char*>("20201");
	char* pszConf = const_cast<char*>("../conf/GlbAdpTanx.conf");

	struct option stOptions[] = {
		{"conf", 1, 0, 'c'},
		{"port", 1, 0, 'p'},
		{"thread", 1, 0, 't'},
		{"version", 0, 0, 'v'},
		{"help", 0, 0, 'h'},
		{0, 0, 0, 0},
	};

	while (true) {
		iReturn = getopt_long(argc, argv, "c:p:t:vh", stOptions, &iIndex);
		if (iReturn < 0) {
			break;
		}
		switch (iReturn) {
			case 'c':
				pszConf = optarg;
				break;
			case 'p':
				pszPort = optarg;
				break;
			case 't':
				iThread = atoi(optarg);
				break;
			case 'v':
				GLB_PRINT("Copyright (C) 2016 Gong Li Bin	\n");
				GLB_PRINT("%s Build on %s %s.\n", GLB_AT_VERSION, __DATE__, __TIME__);
				return GLB_SUCCESS;
			case 'h':
				GLB_PRINT("Usage: %s [options]\n", argv[0]);
				GLB_PRINT("Valid options are:\n");
				GLB_PRINT("[0] -c or --conf:       set conf\n");
				GLB_PRINT("[0] -p or --port:       set port\n");
				GLB_PRINT("[0] -t or --thread:     set pool\n");
				GLB_PRINT("[0] -v or --version:    show version\n");
				GLB_PRINT("[0] -h or --help:       show help\n");
				GLB_PRINT("Copyright (C) 2016 Gong Li Bin	\n");
				GLB_PRINT("%s Build on %s %s.\n", GLB_AT_VERSION, __DATE__, __TIME__);
				return GLB_SUCCESS;
			default:
				return GLB_SUCCESS;
		}
	}

	signal(SIGINT, GlbAdpTanxQuit);
	signal(SIGSEGV, GlbAdpTanxQuit);
	signal(SIGTERM, GlbAdpTanxQuit);

	/* conf */
	GLB_PRINT("Will GlbAdpTanxConfig...\n");
	if (GLB_SUCCESS == GlbAdpTanxCfgLoad(pszConf)) {
		#ifdef _GLB_DEBUG_
			GlbAdpTanxCfgPrint();
		#endif
	}
	else {
		GLB_ERROR("Failed to GlbAdpTanxConfig\n");
		return GLB_FAILURE;
	}

	/* fork */
	#ifdef _GLB_DEBUG_
		iThread = 1;
	#else
		GlbFork();
	#endif

	/* msg */
	GLB_PRINT("Will CMsg.GlbIpcCreate...\n");
	sg_pCMsg = new GlbIpc::CGlbMsg[GLB_AD_MSG];
	GlbIpcMsgKernel(GLB_AD_MAX, GLB_AD_MNB, GLB_AD_MNI);
	for (; iCount < GLB_AD_MSG; iCount ++) {
		if (GLB_SUCCESS != (sg_pCMsg + iCount)->GlbIpcCreate((sg_pCMsg + iCount)->GlbIpcKey(GLB_AD_PATH, GLB_AD_KEY + iCount))) {
			GLB_ERROR("Failed to GlbIpcCreate[%d]\n", iCount);
			return GLB_FAILURE;
		}
	}

	/* pool */
	GLB_PRINT("Will CPool.GlbPoolCreate...\n");
	sg_CPool.GlbPoolAttrInit();
	sg_CPool.GlbPoolTotalSet(iThread);
	sg_CPool.GlbPoolRoutineSet(GlbAdpTanxPoolCore);
	if (GLB_SUCCESS != sg_CPool.GlbPoolCreate()) {
		GLB_ERROR("Failed to GlbPoolCreate\n");
		return GLB_FAILURE;
	}

	/* epoll */
	GLB_PRINT("Will CEpoll.GlbEpollTcpInit...\n");
	CNet.GlbNetLimit(GLB_SOCK_MAX);
	if (GLB_SUCCESS == CEpoll.GlbEpollTcpInit(pszPort)) {
		CEpoll.GlbEpollSetFunction(GlbAdpTanxEpollCore);
		CEpoll.GlbEpollTcpWait();
	}

	return GLB_SUCCESS;
}

void GlbAdpTanxQuit(int iSignal)
{
	GLB_ERROR("GlbAdpTanx-1.0.0 stopped by signal %d\n", iSignal);

	exit(GLB_FAILURE);
}

int GlbAdpTanxCfgLoad(char* pszConf)
{
	GLBATENG_S stEng;
	char* pszKey = NULL;
	char* pszVal = NULL;
	GlbCls::CGlbConf CConf;
	char szLine[GLB_BYTE512] = { 0 };

	if (GLB_SUCCESS == CConf.GlbConfOpen(pszConf)) {
		/* DEV */
		if (GLB_SUCCESS == CConf.GlbConfGetVal(GLB_AT_GLB, GLB_AT_DEV, sg_stConf.m_szDev)) {
			/* USEC */
			if (GLB_SUCCESS == CConf.GlbConfGetVal(GLB_AT_GLB, GLB_AT_SEC, sg_stConf.m_szUsec)) {
			}
			else {
				goto GlbError;
			}
		}
		else {
			goto GlbError;
		}
		/* LOGFILE */
		if (GLB_SUCCESS == CConf.GlbConfGetVal(GLB_AT_LOG, GLB_AT_REQ, sg_stConf.m_szLogReq)) {
			if (GLB_SUCCESS == CConf.GlbConfGetVal(GLB_AT_LOG, GLB_AT_RES, sg_stConf.m_szLogRes)) {
			}
			else {
				goto GlbError;
			}
		}
		else {
			goto GlbError;
		}
		/* GLBENGINE */
		if (GLB_SUCCESS == sg_stConf.m_CEng.GlbSetInit(GLB_AT_SET, sizeof(GLBATENG_S))) {
			if (GLB_SUCCESS == CConf.GlbConfGetSec(GLB_AT_ENG)) {
				while (GLB_SUCCESS == CConf.GlbConfGetLine(szLine, GLB_BYTE512)) {
					if (true == CConf.GlbConfIsValid(szLine)) {
						CConf.GlbConfTrim(szLine);
						if (GLB_SUCCESS == CConf.GlbConfKeyValue(szLine, &pszKey, &pszVal)) {
							memset(&stEng, '\0', sizeof(stEng));
							strncpy(stEng.m_szDev, pszKey, strlen(pszKey));
							strncpy(stEng.m_szUrl, pszVal, strlen(pszVal));
							sg_stConf.m_CEng.GlbSetIndexPut(sg_stConf.m_CEng.GlbSetGetCount(), &stEng);
						}
					}
					else {
						break;
					}
				}
			}
			else {
				goto GlbError;
			}
		}
		else {
			goto GlbError;
		}

		CConf.GlbConfClose();
	}
	else {
		return GLB_FAILURE;
	}

	return GLB_SUCCESS;

GlbError:
	CConf.GlbConfClose();

	return GLB_FAILURE;
}

void GlbAdpTanxCfgPrint()
{
	UINT uiCount = 0;
	GLBATENG_S* pstEng = NULL;

	GLB_PRINT("DEV: %s\n", sg_stConf.m_szDev);
	GLB_PRINT("USEC: %s\n", sg_stConf.m_szUsec);
	GLB_PRINT("REQLOG: %s\n", sg_stConf.m_szLogReq);
	GLB_PRINT("RESLOG: %s\n", sg_stConf.m_szLogRes);
	for (; uiCount < sg_stConf.m_CEng.GlbSetGetCount(); uiCount ++) {
		pstEng = (GLBATENG_S*)sg_stConf.m_CEng.GlbSetIndexGet(uiCount);
		GLB_PRINT("GLBENGINE: %s = %s\n", pstEng->m_szDev, pstEng->m_szUrl);
	}

	return;
}

int GlbAdpTanxEpollCore(int iSocket)
{
	GlbNet::CGlbTcp CTcp;
	int iLength = GLB_FAILURE;
	GLBATPARA_S* pstPara = NULL;
	UCHAR uszPacket[GLB_KBYTES4] = { 0 };

	signal(SIGPIPE, SIG_IGN);
	CTcp.GlbNetSetSocket(iSocket);
	if ((iLength = CTcp.GlbNetRecv(uszPacket, GLB_KBYTES4)) > 0) {
		if (NULL != (pstPara = GlbAdpTanxMalloc(GLB_KBYTES4))) {
			pstPara->m_iSocket = iSocket;
			pstPara->m_iLength = iLength;
			memcpy(pstPara->m_puszData, uszPacket, iLength);
			sg_CPool.GlbPoolDistribute(pstPara);
		}
	}

	return iLength;
}

void* GlbAdpTanxPoolCore(void* pPara)
{
	int iEng = 0;
	UINT uiMsg = 0;
	GlbNet::CGlbTcp CTcp;
	int iReturn = GLB_SUCCESS;
	GLBATPARA_S* pstPara = (GLBATPARA_S*)pPara;

	#ifdef _GLB_DEBUG_
		CGlbTime CTime;
		CTime.GlbTimeOn();
		GLB_PRINT("\nGlbAdpTanxPoolCore[%lu] start\n", pthread_self());
	#endif

	/* request */
	if (GLB_FAILURE != (iEng = GlbAdpTanxParse(pstPara))) {
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("Will GlbAdpTanxRtb to %d...\n", iEng);
		#endif
		/* rtb */
		iReturn = GlbAdpTanxRtb(iEng, pstPara);
	}
	else {
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("Will GlbAdpTanxEmpty...\n");
		#endif
	}

	/* response */
	CTcp.GlbNetSetSocket(pstPara->m_iSocket);
	CTcp.GlbNetSend(pstPara->m_puszData, pstPara->m_iLength);

	/* messages */
	pstPara->m_stReq.m_ucType = GLB_AD_REQ;
	if (pstPara->m_stRes.m_iLength > GLB_PRO_HEAD) {
		pstPara->m_stRes.m_ucType = GLB_AD_RES;
		if (GLB_SUCCESS == iReturn) {
			pstPara->m_stReq.m_ucType = GLB_AD_VIL;
		}
	}
	if ('\0' != pstPara->m_stReq.m_ucType) {
		uiMsg = __sync_add_and_fetch(&sg_uiMsg, 1) % GLB_AD_MSG;
		if (GLB_SUCCESS != (sg_pCMsg + uiMsg)->GlbMsgPut((void*)&pstPara->m_stReq, sizeof(GLBADPMSG_S), GLB_IPC_NOWAIT)) {
			/*#ifdef _GLB_DEBUG_
				GLB_DEBUG("Write MsgReq to GlbAdpTanxMsgLog.tmp\n");
			#endif
			GlbAdpTanxMsgLog(&pstPara->m_stReq);*/
		}
		if ('\0' != pstPara->m_stRes.m_ucType) {
			if (GLB_SUCCESS != (sg_pCMsg + uiMsg)->GlbMsgPut((void*)&pstPara->m_stRes, sizeof(GLBADPMSG_S), GLB_IPC_NOWAIT)) {
				/*#ifdef _GLB_DEBUG_
					GLB_DEBUG("Write MsgRes to GlbAdpTanxMsgLog.tmp\n");
				#endif
				GlbAdpTanxMsgLog(&pstPara->m_stRes);*/
			}
		}
	}

	#ifdef _GLB_DEBUG_
		GLB_PRINT("GlbAdpTanxPoolCore[%lu] end Use time ", pthread_self());
		CTime.GlbTimeOff();
		CTime.GlbTimeDisplay();
	#endif

	return GlbAdpTanxFree((GLBATPARA_S*)pPara);
}

int GlbAdpTanxParse(GLBATPARA_S* pstPara)
{
	int iAdz = 0;
	int iBody = 0;
	int iComp = 0;
	int iCount = 0;
	GLBATREQ_C CReq;
	GlbCls::CGlbMd5 CMd5;
	GlbNet::CGlbPro CPro;
	GlbNet::CGlbUdp CUdp;
	GlbNet::GLBPROKV_S stKv;
	int iHash = GLB_FAILURE;
	GLBATREQAD_C* pCAdz = NULL;
	UCHAR uszMd5[GLB_MD5] = { 0 };
	char szVal[GLB_KBYTES2] = { 0 };
	UCHAR uszComp[GLB_KBYTES4] = { 0 };
	char* pszBody = (char*)pstPara->m_puszData;
	GlbNet::GLBPROHEAD_S stHead = {
		GLB_PRO_FLAG,
		GLB_PRO_1,
		GLB_PRO_DATA,
		GLB_PRO_0,
		GLB_PRO_Y,
		GLB_PRO_0,
		GLB_RD_TANX,
		GLB_RD_REQ,
		GLB_AT_COMP,
		GLB_DEF_SUCC,
		GLB_PRO_0,
		GLB_PRO_0,
	};

	/* http */
	if (GLB_SUCCESS == strncasecmp(pszBody, GLB_AT_POST, strlen(GLB_AT_POST))) {
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("Method: %s\n", GLB_AT_POST);
		#endif
		/* leng */
		if (NULL != (pszBody = strcasestr(pszBody, GLB_AT_LENG))) {
			pszBody += strlen(GLB_AT_LENG);
			iBody = atoi(pszBody);
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("BodyLength: %d\n", iBody);
			#endif

			/* body */
			if (NULL != (pszBody = strstr(pszBody, GLB_AT_RNRN))) {
				pszBody += strlen(GLB_AT_RNRN);
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("BodyOffset: %d\n", (int)(pszBody - (char*)pstPara->m_puszData));
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("BodyOffset: 0\n");
				#endif
			}
		}
		else {
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("BodyLength: 0\n");
			#endif
		}
	}
	else {
		#ifdef _GLB_DEBUG_
			GLB_DEBUG("Method: error\n");
		#endif
		pszBody = NULL;
	}

	/* tanx */
	if (NULL != pszBody) {
		if (true == CReq.ParseFromArray(pszBody, iBody)) {
			iComp = 0;
			memset(uszComp, '\0', GLB_KBYTES4);

			#ifdef _GLB_DEBUG_
				GLB_DEBUG("TanxRequest {\n");
			#endif

			// required int32 version = 1;
			if (true == CReq.has_version()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("version: ");
					std::cout << CReq.version() << std::endl;
				#endif
				sprintf(pstPara->m_szVer, "%d", CReq.version());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("version: null\n");
				#endif
			}

			// required string bid = 2;
			if (true == CReq.has_bid()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("bid: ");
					std::cout << CReq.bid() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_BDID, CReq.bid());
				memcpy(pstPara->m_szBid, CReq.bid().c_str(), CReq.bid().length());
  			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("bid: null\n");
				#endif
			}

			// optional uint32 is_ping = 12 [default = 0];
			if (true == CReq.has_is_ping()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("is_ping: ");
					std::cout << CReq.is_ping() << std::endl;
				#endif
				if (0 != CReq.is_ping()) {
					goto GlbError;
				}
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("is_ping: null\n");
				#endif
			}

			// optional string tid = 3;
			if (true == CReq.has_tid()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("tid: ");
					std::cout << CReq.tid() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_GUID, CReq.tid());
				CMd5.GlbMd5Run((UCHAR*)CReq.tid().c_str(), CReq.tid().length(), uszMd5);
				iHash = (*(ULONGLONG*)uszMd5 ^ *(ULONGLONG*)(uszMd5 + GLB_MD5 / 2)) % sg_stConf.m_CEng.GlbSetGetCount();
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("tid: null\n");
				#endif
			}

			// optional string ip = 4;
			if (true == CReq.has_ip()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("ip: ");
					std::cout << CReq.ip() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_ADDR, CReq.ip());
				if (GLB_FAILURE == iHash) {
					CMd5.GlbMd5Run((UCHAR*)CReq.ip().c_str(), CReq.ip().length(), uszMd5);
					iHash = (*(ULONGLONG*)uszMd5 ^ *(ULONGLONG*)(uszMd5 + GLB_MD5 / 2)) % sg_stConf.m_CEng.GlbSetGetCount();
				}
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("ip: null\n");
				#endif
			}

			// optional string user_agent = 5;
			if (true == CReq.has_user_agent()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("user_agent: ");
					std::cout << CReq.user_agent() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_UAGLB, CReq.user_agent());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("user_agent: null\n");
				#endif
			}

			// repeated string excluded_click_through_url = 6;
			if (CReq.excluded_click_through_url_size() > 0) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("excluded_click_through_url: ");
				#endif
				for(iCount = 0; iCount < CReq.excluded_click_through_url_size(); iCount ++) {
					#ifdef _GLB_DEBUG_
						std::cout << iCount << ". " << CReq.excluded_click_through_url(iCount);
					#endif
					GLB_AT_STRING(GLB_RD_EURL, CReq.excluded_click_through_url(iCount));
				}
				#ifdef _GLB_DEBUG_
					GLB_PRINT("\n");
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("excluded_click_through_url: null\n");
				#endif
			}

			// optional string url = 7;
			if (true == CReq.has_url()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("url: ");
					std::cout << CReq.url() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_CURL, CReq.url());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("url: null\n");
				#endif
			}

			// optional uint32 category = 8;
			if (true == CReq.has_category()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("category: ");
					std::cout << CReq.category() << std::endl;
				#endif
				GLB_AT_UINT32(GLB_RD_WCAT, CReq.category());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("category: null\n");
				#endif
			}

			// optional uint32 adx_type = 9 [default = 0];
			if (true == CReq.has_adx_type()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("adx_type: ");
					std::cout << CReq.adx_type() << std::endl;
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("adx_type: NULL\n");
				#endif
			}

			// repeated AdzInfo adzinfo = 10;
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("adzinfo: ");
			#endif
			for (iAdz = 0; iAdz < CReq.adzinfo_size(); iAdz ++) {
				pCAdz = const_cast<GLBATREQAD_C*>(&CReq.adzinfo(iAdz));

				// required uint32 id = 1; 
				if (true == pCAdz->has_id()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{id: " << pCAdz->id() << "} + ";
					#endif
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{id: null} + ");
					#endif
				}

				// required string pid = 2; 
				if (true == pCAdz->has_pid()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{pid: " << pCAdz->pid() << "} + ";
					#endif
					GLB_AT_STRING(GLB_RD_ADPD, pCAdz->pid());
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{pid: null} + ");
					#endif
				}

				// optional string size = 3;
				if (true == pCAdz->has_size()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{size: " << pCAdz->size() << "} + ";
					#endif
					GLB_AT_STRING(GLB_RD_ADSZ, pCAdz->size());
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{size: null} + ");
					#endif
				}

				// optional uint32 ad_bid_count = 4 [default = 2];
				if (true == pCAdz->has_ad_bid_count()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{ad_bid_count: " << pCAdz->ad_bid_count() << "} + ";
					#endif
					GLB_AT_UINT32(GLB_RD_ADBC, pCAdz->ad_bid_count());
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{ad_bid_count: null} + ");
					#endif
				}

				// repeated uint32 view_type = 5;
				if (pCAdz->view_type_size() > 0) {
					for (iCount = 0; iCount < pCAdz->view_type_size(); iCount ++) {
						#ifdef _GLB_DEBUG_
							std::cout << "{view_type: " << pCAdz->view_type(iCount) << "} + ";
						#endif
						GLB_AT_UINT32(GLB_RD_ADVT, pCAdz->view_type(iCount));
					}
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{view_type: null} + ");
					#endif
				}

				// repeated uint32 excluded_filter = 6;
				if (pCAdz->excluded_filter_size() > 0) {
					for (iCount = 0; iCount < pCAdz->excluded_filter_size(); iCount ++) {
						#ifdef _GLB_DEBUG_
							std::cout << "{excluded_filter: " << pCAdz->excluded_filter(iCount) << "} + ";
						#endif
						GLB_AT_UINT32(GLB_RD_ADFT, pCAdz->excluded_filter(iCount));
					}
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{excluded_filter: null} + ");
					#endif
				}

				// optional uint32 min_cpm_price = 7;
				if (true == pCAdz->has_min_cpm_price()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{min_cpm_price: " << pCAdz->min_cpm_price() << "} + ";
					#endif
					GLB_AT_UINT32(GLB_RD_ADMN, pCAdz->min_cpm_price());
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{min_cpm_price: null} + ");
					#endif
				}

				// optional Location adz_location = 8 [default = NA];
				if (true == pCAdz->has_adz_location()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{adz_location: " << pCAdz->adz_location() << "} + ";
					#endif
					GLB_AT_UINT32(GLB_RD_ADLC, pCAdz->adz_location());
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{adz_location: null} + ");
					#endif
				}

				// optional ViewScreen view_screen = 9 [default = SCREEN_NA];
				if (true == pCAdz->has_view_screen()) {
					#ifdef _GLB_DEBUG_
						std::cout << "{view_screen: " << pCAdz->view_screen() << "}; ";
					#endif
					GLB_AT_UINT32(GLB_RD_ADVS, pCAdz->view_screen());
				}
				else {
					#ifdef _GLB_DEBUG_
						GLB_PRINT("{view_screen: null}; ");
					#endif
				}
			}
			#ifdef _GLB_DEBUG_
				GLB_PRINT("\n");
			#endif

			// optional uint32 is_test = 11 [default = 0];
			if (true == CReq.has_is_test()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("is_test: ");
					std::cout << CReq.is_test() << std::endl;
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("is_test: null\n");
				#endif
			}

			// optional int32 timezone_offset = 13;
			if (true == CReq.has_timezone_offset()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("timezone_offset: ");
					std::cout << CReq.timezone_offset() << std::endl;
				#endif
				GLB_AT_UINT32(GLB_RD_TTZO, CReq.timezone_offset());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("timezone_offset: null\n");
				#endif
			}

			// repeated int32 user_vertical = 14;
			if (CReq.user_vertical_size() > 0) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("user_vertical: ");
				#endif
				for(iCount = 0; iCount < CReq.user_vertical_size(); iCount ++) {
					#ifdef _GLB_DEBUG_
						std::cout << iCount << ". " << CReq.user_vertical(iCount);
					#endif
					GLB_AT_UINT32(GLB_RD_TUVT, CReq.user_vertical(iCount));
				}
				#ifdef _GLB_DEBUG_
					GLB_PRINT("\n");
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("user_vertical: null\n");
				#endif
			}

			// optional string anonymous_id = 15;
			if (true == CReq.has_anonymous_id()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("bid: ");
					std::cout << CReq.anonymous_id() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_TAID, CReq.anonymous_id());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("anonymous_id: null\n");
				#endif
			}

			// optional string detected_language = 16;
			if (true == CReq.has_detected_language()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("detected_language: ");
					std::cout << CReq.detected_language() << std::endl;
				#endif
				GLB_AT_STRING(GLB_RD_TDLG, CReq.detected_language());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("detected_language: null\n");
				#endif
			}

			// repeated int32 excluded_sensitive_category = 17;
			if (CReq.excluded_sensitive_category_size() > 0) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("excluded_sensitive_category: ");
				#endif
				for(iCount = 0; iCount < CReq.excluded_sensitive_category_size(); iCount ++) {
					#ifdef _GLB_DEBUG_
						std::cout << iCount << ". " << CReq.excluded_sensitive_category(iCount);
					#endif
					GLB_AT_UINT32(GLB_RD_TSEN, CReq.excluded_sensitive_category(iCount));
				}
				#ifdef _GLB_DEBUG_
					GLB_PRINT("\n");
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("excluded_sensitive_category: null\n");
				#endif
			}

			// optional int32 category_version = 18;
			if (true == CReq.has_category_version()) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("category_version: ");
					std::cout << CReq.category_version() << std::endl;
				#endif
				GLB_AT_UINT32(GLB_RD_TCTV, CReq.category_version());
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("category_version: null\n");
				#endif
			}

			// repeated int32 excluded_ad_category = 20;
			if (CReq.excluded_ad_category_size() > 0) {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("excluded_ad_category: ");
				#endif
				for(iCount = 0; iCount < CReq.excluded_ad_category_size(); iCount ++) {
					#ifdef _GLB_DEBUG_
						std::cout << iCount << ". " << CReq.excluded_ad_category(iCount);
					#endif
					GLB_AT_UINT32(GLB_RD_TADC, CReq.excluded_ad_category(iCount));
				}
				#ifdef _GLB_DEBUG_
					GLB_PRINT("\n");
				#endif
			}
			else {
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("excluded_ad_category: null\n");
				#endif
			}

			#ifdef _GLB_DEBUG_
				GLB_DEBUG("}\n");
			#endif

			/* body */
			GlbAdpTanxCompress(uszComp, iComp, pstPara->m_stReq.m_uszMsg + GLB_PRO_HEAD, &stHead.m_iLength);
			pstPara->m_stReq.m_iLength = stHead.m_iLength + GLB_PRO_HEAD;

			/* head */
			CPro.GlbProInduce(&stHead);
			CPro.GlbProCreate(pstPara->m_stReq.m_uszMsg, GLB_PRO_HEAD);
		}
		else {
			#ifdef _GLB_DEBUG_
				GLB_DEBUG("Failed to ParseFromArray\n");
			#endif
		}
	}

	return iHash;

GlbError:
	GlbAdpTanxEmpty(pstPara);

	return iHash;
}

int GlbAdpTanxRtb(int iHash, GLBATPARA_S* pstPara)
{
	int iVal = 0;
	int iComp = 0;
	UINT uiWid = 0;
	UINT uiHei = 0;
	USHORT usCount = 0;
	GLBATRES_C CRes;
	std::string STanx;
	GLBATINFO_S stInfo;
	GlbNet::CGlbPro CPro;
	GlbNet::CGlbUdp CUdp;
	UCHAR* puszInfo = NULL;
	GLBATENG_S* pstEng = NULL;
	GLBATRESAD_C* pCAds = NULL;
	char szVal[GLB_KBYTES2] = { 0 };
	char szHead[GLB_KBYTES2] = { 0 };
	UCHAR uszComp[GLB_KBYTES4] = { 0 };
	char szSnippet[GLB_KBYTES4] = { 0 };
	static UINT s_uiUsec = atoi(sg_stConf.m_szUsec);
	GlbNet::GLBPROKV_S stKv[GLB_AT_KVS] = {{'\0', 0, NULL}};

	pstEng = (GLBATENG_S*)sg_stConf.m_CEng.GlbSetIndexGet(iHash);
	if (GLB_SUCCESS == CUdp.GlbNetInitSend(pstEng->m_szUrl)) {
		CUdp.GlbNetSetTimeOut(0, s_uiUsec);
		if (CUdp.GlbNetSend(pstPara->m_stReq.m_uszMsg, pstPara->m_stReq.m_iLength) > 0) {
			pstPara->m_stRes.m_iLength = CUdp.GlbNetRecv(pstPara->m_stRes.m_uszMsg, GLB_KBYTES4);
		}
		else {
			GLB_ERROR("Failed to GlbNetSend\n");
		}
		CUdp.GlbNetClose();
	}
	else {
		GLB_ERROR("Failed to GlbNetInitSend\n");
	}

	if (pstPara->m_stRes.m_iLength > GLB_PRO_0) {
		CPro.GlbProParse(pstPara->m_stRes.m_uszMsg, pstPara->m_stRes.m_iLength);
		if (GLB_DEF_SUCC == CPro.GlbProGetResult()) {
			GlbAdpTanxUnCompress(CPro.GlbProGetBody(), CPro.GlbProGetLength(), uszComp, &iComp);
			GlbProKvInput(stKv, CPro.GlbProGetTotal(), uszComp, iComp);
			if (NULL != (pCAds = CRes.add_ads())) {
				pCAds->set_adzinfo_id(0);
				pCAds->set_ad_bid_count_idx(0);
				CRes.set_bid(pstPara->m_szBid);
				CRes.set_version(atoi(pstPara->m_szVer));
				memset(&stInfo, '\0', sizeof(GLBATINFO_S));
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("TanxResponse {\n");
					GLB_DEBUG("version: %s\n", pstPara->m_szVer);
					GLB_DEBUG("bid: %s\n", pstPara->m_szBid);
					GLB_DEBUG("adzinfo_id: 0\n");
					GLB_DEBUG("ad_bid_count_idx: 0\n");
				#endif
				while (usCount < CPro.GlbProGetTotal()) {
					switch (stKv[usCount].m_ucKey) {
						case GLB_RD_ADID:
							// required uint32 id = 1; 
							break;
						case GLB_RD_ADMX:
							// required uint32 max_cpm_price = 2;
							GLB_AT_VALUE(stInfo.m_szMcp);
							pCAds->set_max_cpm_price(atoi(stInfo.m_szMcp));
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("max_cpm_price: %s\n", stInfo.m_szMcp);
							#endif
							break;
						case GLB_RD_ADBI:
							// optional uint32 ad_bid_count_idx = 3;
							break;
						case GLB_RD_ADHS:
							// optional string html_snippet = 4;
							GLB_AT_VALUE(stInfo.m_szSnp);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("snippet: %s\n", stInfo.m_szSnp);
							#endif
							break;
						case GLB_RD_ADTU:
							// repeated string click_through_url = 5;
							GLB_AT_VALUE(szVal);
							pCAds->add_click_through_url(szVal);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("click_through_url: %s\n", szVal);
							#endif
							break;
						case GLB_RD_WCAT:
							// repeated int32 category = 6;
							GLB_AT_VALUE(szVal);
							pCAds->add_category(atoi(szVal));
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("category: %s\n", szVal);
							#endif
							break;
						case GLB_RD_ADCT:
							// repeated int32 creative_type = 7;
							GLB_AT_VALUE(szVal);
							pCAds->add_creative_type(atoi(szVal));
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("creative_type: %s\n", szVal);
							#endif
							break;
						case GLB_RD_ADNG:
							// optional string network_guid = 8;
							break;
						case GLB_RD_ADED:
							// optional string extend_data = 9;
							break;
						case GLB_RD_ADDU:
							// repeated string destination_url = 10;
							GLB_AT_VALUE(szVal);
							pCAds->add_destination_url(szVal);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("destination_url: %s\n", szVal);
							#endif
							break;
						case GLB_RD_ADCI:
							// optional string creative_id = 11;
							GLB_AT_VALUE(stInfo.m_szCID);
							pCAds->set_creative_id(stInfo.m_szCID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("creative_id: %s\n", stInfo.m_szCID);
							#endif
							break;
						case GLB_RD_ODID:
							GLB_AT_VALUE(stInfo.m_szOID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("order_id: %s\n", stInfo.m_szOID);
							#endif
							break;
						case GLB_RD_EXID:
							GLB_AT_VALUE(stInfo.m_szEID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("execute_id: %s\n", stInfo.m_szEID);
							#endif
							break;
						case GLB_RD_MTID:
							GLB_AT_VALUE(stInfo.m_szMID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("material_id: %s\n", stInfo.m_szMID);
							#endif
							break;
						case GLB_RD_GUID:
							GLB_AT_VALUE(stInfo.m_szUID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("user id: %s\n", stInfo.m_szUID);
							#endif
							break;
						case GLB_RD_BDID:
							GLB_AT_VALUE(stInfo.m_szBID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("bid: %s\n", stInfo.m_szBID);
							#endif
							break;
						case GLB_RD_ADSZ:
							GLB_AT_VALUE(stInfo.m_szSiz);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("size: %s\n", stInfo.m_szSiz);
							#endif
							break;
						case GLB_RD_ADPD:
							GLB_AT_VALUE(stInfo.m_szPID);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("pid: %s\n", stInfo.m_szPID);
							#endif
							break;
						case GLB_RD_DEV:
							GLB_AT_VALUE(stInfo.m_szDev);
							#ifdef _GLB_DEBUG_
								GLB_DEBUG("region: %s\n", stInfo.m_szDev);
							#endif
							break;
						default:
							break;
					}
					usCount += 1;
				}
				#ifdef _GLB_DEBUG_
					GLB_DEBUG("}\n");
				#endif

				memset(szVal, '\0', GLB_KBYTES2);
				sprintf(stInfo.m_szLID, "%d", CPro.GlbProGetType());
				sprintf(szVal, GLB_AT_INFO, stInfo.m_szOID, stInfo.m_szEID, stInfo.m_szCID, stInfo.m_szMID,
					stInfo.m_szUID, stInfo.m_szBID, stInfo.m_szLID, stInfo.m_szMcp, stInfo.m_szDev, stInfo.m_szPID);
				if (NULL != (puszInfo = GlbBaseEncode((UCHAR*)szVal, strlen(szVal), &iVal))) {
					GlbAdpTanxAdSize(stInfo.m_szSiz, &uiWid, &uiHei);
					while (0x3d == *(puszInfo + (-- iVal))) *(puszInfo + iVal) = '\0';
					sprintf(szSnippet, GLB_AT_SNIP, uiWid, uiHei, stInfo.m_szSnp, stInfo.m_szPID, stInfo.m_szBID, (char*)puszInfo);
					pCAds->set_html_snippet(szSnippet);
					GlbFree((void**)&puszInfo);
				}

				CRes.SerializeToString(&STanx);

				/* head */
				sprintf(szHead, GLB_AT_HTTP200, CRes.ByteSize());
				memcpy(pstPara->m_puszData, szHead, pstPara->m_iLength = strlen(szHead));

				/* body */
				memcpy(pstPara->m_puszData + pstPara->m_iLength, STanx.c_str(), CRes.ByteSize());
				pstPara->m_iLength += CRes.ByteSize();
			}
			else {
				GLB_ERROR("Failed to add_ads\n");
				goto GlbError;
			}
		}
		else {
			GLB_ERROR("Failed to GlbProGetResult\n");
			goto GlbError;
		}
	}
	else {
		GLB_ERROR("Failed to GlbNetRecv\n");
		goto GlbError;
	}

	return GLB_SUCCESS;

GlbError:
	GlbAdpTanxEmpty(pstPara);

	return GLB_FAILURE;
}

void GlbAdpTanxEmpty(GLBATPARA_S* pstPara)
{
	GLBATRES_C CRes;
	std::string STanx;
	char szHead[GLB_KBYTES2] = { 0 };

	/* tanx */
	CRes.set_version(atoi(pstPara->m_szVer));
	CRes.set_bid(pstPara->m_szBid);
	CRes.SerializeToString(&STanx);

	/* head */
	sprintf(szHead, GLB_AT_HTTP200, CRes.ByteSize());
	memcpy(pstPara->m_puszData, szHead, pstPara->m_iLength = strlen(szHead));

	/* body */
	memcpy(pstPara->m_puszData + pstPara->m_iLength, STanx.c_str(), CRes.ByteSize());
	pstPara->m_iLength += CRes.ByteSize();

	return;
}

void GlbAdpTanxMsgLog(GLBADPMSG_S* pstMsg)
{
	GlbCls::CGlbFile CFile;
	char szLog[GLB_BUFFER] = { 0 };
	char szFile[GLB_BUFFER] = { 0 };
	UCHAR uszPacket[GLB_KBYTES4] = { 0 };

	memcpy(uszPacket, &pstMsg->m_iLength, sizeof(int));
	memcpy(uszPacket + sizeof(int), &pstMsg->m_ucType, sizeof(UCHAR));
	memcpy(uszPacket + sizeof(int) + sizeof(UCHAR), pstMsg->m_uszMsg, pstMsg->m_iLength);

	sprintf(szFile, "%stmp", GLB_AD_MSGLOG);
	if (GLB_SUCCESS == CFile.GlbFileOpen(szFile, GLB_O_CREATE | GLB_O_WRONLY | GLB_O_APPEND)) {
		CFile.GlbFileWrite(uszPacket, sizeof(int) + sizeof(UCHAR) + pstMsg->m_iLength, false);
		CFile.GlbFileClose();
	}

	if (GLB_AD_TOTAL < __sync_add_and_fetch(&sg_uiTotal, 1)) {
		sprintf(szLog, "%s", GLB_AD_MSGLOG);
		GlbLocalDayTime(szLog + strlen(szLog));
		rename(szFile, szLog);
		sg_uiTotal = 0;
	}

	return;
}

void GlbAdpTanxAdSize(char* pszSize, UINT* puiWid, UINT* puiHei)
{
	char* pszCur = pszSize;

	while ('x' != *pszCur ++) {}
	*puiWid = atoi(pszSize);
	*puiHei = atoi(pszCur);

	return;
}

GLBATPARA_S* GlbAdpTanxMalloc(int iLength)
{
	GLBATPARA_S* pstPara = NULL;

	if (NULL != (pstPara = (GLBATPARA_S*)GlbMalloc(sizeof(GLBATPARA_S)))) {
		if (NULL == (pstPara->m_puszData = (UCHAR*)GlbMalloc(iLength))) {
			GlbFree((void**)&pstPara);
		}
	}

	return pstPara;
}

void* GlbAdpTanxFree(GLBATPARA_S* pstPara)
{
	if (NULL != pstPara) {
		GlbFree((void**)&pstPara->m_puszData);
		GlbFree((void**)&pstPara);
	}

	return pstPara;
}
